/* =========================================
 *           文件存储子服务实现 
 * =========================================*/
#pragma once
#include "etcd.hpp"
#include "logger.hpp"
#include "utils.hpp"
#include "file.pb.h"
#include "base.pb.h"
#include <brpc/server.h>
#include <string>
#include <sys/stat.h>

namespace file {

class FileServiceImpl;
class FileServer;
class FileServerBuilder;
using FileServiceImplPtr = shared_ptr<FileServiceImpl>;
using FileServerPtr = shared_ptr<FileServer>;
using FileServerBuilderPtr = shared_ptr<FileServerBuilder>;

class FileServiceImpl : public im_proto::FileService
{
    typedef google::protobuf::Map<std::string, im_proto::FileDownloadData> GPMap;
public:
    FileServiceImpl(const std::string& storagePath) : storagePath_(storagePath) 
    {
        umask(0);   // 设置文件掩码
        mkdir(storagePath.data(), 0775);
        if(storagePath.back() != '/') {
            storagePath_.push_back('/');
        }
    }
    ~FileServiceImpl() {}

    // 获取单文件
    void GetSingleFile(google::protobuf::RpcController* controller,
                        const ::im_proto::GetSingleFileReq* request,
                        ::im_proto::GetSingleFileRsp* response,
                        ::google::protobuf::Closure* done) 
    {   
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());
        
        std::string fileId = request->file_id();
        std::string filename = storagePath_ + fileId;
        std::string body;
        // 将服务器文件数据读取返回
        if(shared::FileUtil::read(filename, body) == false) {
            response->set_success(false);
            response->set_errmsg("读取文件数据失败!");
            LOG_ERROR("{} 读取文件数据失败!", request->request_id());
            return;
        }   
        response->set_success(true);
        response->mutable_file_data()->set_file_id(fileId);
        response->mutable_file_data()->set_file_content(body);
    }
    // 获取多文件
    void GetMultiFile(google::protobuf::RpcController* controller,
                    const ::im_proto::GetMultiFileReq* request,
                    ::im_proto::GetMultiFileRsp* response,
                    ::google::protobuf::Closure* done) 
    {
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());
        
        int fileSize = request->file_id_list_size();
        for(const auto& fileId : request->file_id_list()) {
            std::string filename = storagePath_ + fileId;
            std::string body;
            if(shared::FileUtil::read(filename, body) == false) {
                response->set_success(false);
                response->set_errmsg("读取文件数据失败!");
                LOG_ERROR("{} 读取文件数据失败!", request->request_id());
                return;
            }
            im_proto::FileDownloadData fileDownloadData;
            fileDownloadData.set_file_id(fileId);
            fileDownloadData.set_file_content(body);
            response->mutable_file_data()->emplace(fileId, fileDownloadData);
        }
        response->set_success(true);
    }
    // 上传单文件
    void PutSingleFile(google::protobuf::RpcController* controller,
                const ::im_proto::PutSingleFileReq* request,
                ::im_proto::PutSingleFileRsp* response,
                ::google::protobuf::Closure* done) 
    {
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        std::string fileId = shared::Uuid::createUuid();
        std::string filename = storagePath_ + fileId;
        // 将文件数据存储服务器
        const im_proto::FileUploadData& fileData = request->file_data();
        if(shared::FileUtil::write(filename, fileData.file_content()) == false) {
            response->set_success(false);
            response->set_errmsg("写入文件数据失败!");
            LOG_ERROR("{} 写入文件数据失败!", request->request_id());
            return;
        }
        response->set_success(true);
        im_proto::FileMessageInfo* fileMessageInfo = response->mutable_file_info();
        fileMessageInfo->set_file_id(fileId);
        fileMessageInfo->set_file_size(fileData.file_size());
        fileMessageInfo->set_file_name(fileData.file_name());
    }
    // 上传多文件
    void PutMultiFile(google::protobuf::RpcController* controller,
                const ::im_proto::PutMultiFileReq* request,
                ::im_proto::PutMultiFileRsp* response,
                ::google::protobuf::Closure* done) 
    {
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());

        for(const auto& fileData : request->file_data()) {
            std::string fileId = shared::Uuid::createUuid();
            std::string filename = storagePath_ + fileId;
            if(shared::FileUtil::write(filename, fileData.file_content()) == false) {
                response->set_success(false);
                response->set_errmsg("写入文件数据失败!");
                LOG_ERROR("{} 写入文件数据失败!", request->request_id());
                return;
            }
            im_proto::FileMessageInfo* fileMessageInfo = response->add_file_info();
            fileMessageInfo->set_file_id(fileId);
            fileMessageInfo->set_file_size(fileData.file_size());
            fileMessageInfo->set_file_name(fileData.file_name());
        }
        response->set_success(true);
    }
private:
    std::string storagePath_;
};

class FileServer
{
public:
    FileServer(const shared::infra::RegistrantPtr& rpcSvrRegist, 
                const std::shared_ptr<brpc::Server>& rpcServer)
        : rpcSvrRegist_(rpcSvrRegist), rpcServer_(rpcServer) {}
    ~FileServer() {}
    void start() { rpcServer_->RunUntilAskedToQuit(); }
private:
    shared::infra::RegistrantPtr rpcSvrRegist_;
    std::shared_ptr<brpc::Server> rpcServer_;
};

class FileServerBuilder
{
public:
    // 构造服务注册客户端对象
    void makeRegistrantObject(const std::string& host, const std::string& serviceName, const std::string& accessHost)
    {
        rpcSvrRegist_ = make_shared<shared::infra::Registrant>(host);
        rpcSvrRegist_->registry(serviceName, accessHost);
    }
    // 构造RPC服务器对象
    void makeRpcServer(uint16_t port, int32_t timeout, uint8_t numThreads, const std::string& storagePath = "./data/")
    {
        rpcServer_ = make_shared<brpc::Server>();
        // 向rpc注册服务
        FileServiceImpl* fileServiceImpl = new FileServiceImpl(storagePath);
        if(rpcServer_->AddService(fileServiceImpl, brpc::ServiceOwnership::SERVER_OWNS_SERVICE) == -1) {
            LOG_ERROR("添加Rpc服务失败!");
            abort();
        }
        // 启动服务器
        brpc::ServerOptions options;    
        options.idle_timeout_sec = timeout;  
        options.num_threads = numThreads;        
        if(rpcServer_->Start(port, &options) == -1) {
            LOG_ERROR("Rpc服务启动失败!");
            abort();
        }
    }
    FileServerPtr build()
    {
        if (!rpcSvrRegist_) {
            LOG_ERROR("还未初始化服务注册模块!");
            abort();
        }
        if (!rpcServer_) {
            LOG_ERROR("还未初始化RPC服务器模块!");
            abort();
        }
        return std::make_shared<FileServer>(rpcSvrRegist_, rpcServer_);
    }
private:
    shared::infra::RegistrantPtr rpcSvrRegist_;
    std::shared_ptr<brpc::Server> rpcServer_;
};


}